package com.lhq.lingsi_study_hall.scoring;

import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.lhq.lingsi_study_hall.manager.AIManager;
import com.lhq.lingsi_study_hall.model.dto.question.QuestionAnswerDTO;
import com.lhq.lingsi_study_hall.model.dto.question.QuestionContentDTO;
import com.lhq.lingsi_study_hall.model.entity.App;
import com.lhq.lingsi_study_hall.model.entity.Question;
import com.lhq.lingsi_study_hall.model.entity.UserAnswer;
import com.lhq.lingsi_study_hall.model.vo.QuestionVO;
import com.lhq.lingsi_study_hall.service.QuestionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.lhq.lingsi_study_hall.constant.PromptConstant.QUESTION_SOLVING_TEST_SYSTEM_PROMPT;
import static com.lhq.lingsi_study_hall.constant.RedisConstant.AI_ANSWER_LOCK;

/**
 * 测评类应用ai评分
 */
@Slf4j
@ScoringStrategyConfig(appType = 1, scoringStrategy = 1)
public class AITestScoringStrategy implements ScoringStrategy{
    @Resource
    private QuestionService questionService;
    @Resource
    private AIManager aiManager;
    @Resource
    private RedissonClient redissonClient;
    /**
     * ai评分结果缓存
     */
    private final Cache<String, String> answerCacheMap =
            Caffeine.newBuilder().initialCapacity(1024)
                    // 缓存5分钟移除
                    .expireAfterAccess(5L, TimeUnit.MINUTES)
                    .build();

    @Override
    public UserAnswer doScore(List<String> choices, App app){
        Long appId = app.getId();
        String jsonStr = JSONUtil.toJsonStr(choices);
        String cacheKey = buildCacheKey(appId, choices);
        String answerJson = answerCacheMap.getIfPresent(cacheKey);
        //1. 判断是否有缓存
        if (StringUtils.isNoneBlank(answerJson)){
            // 命中缓存
            UserAnswer userAnswer = JSONUtil.toBean(answerJson, UserAnswer.class);
            userAnswer.setAppId(appId);
            userAnswer.setAppType(app.getAppType());
            userAnswer.setScoringStrategy(app.getScoringStrategy());
            userAnswer.setChoices(jsonStr);
            return userAnswer;
        }
        //定义锁
        RLock lock = redissonClient.getLock(AI_ANSWER_LOCK + cacheKey);
        try {
            //竞争锁
            boolean tryLock = lock.tryLock(3, 15, TimeUnit.SECONDS);
            if (!tryLock){
                // 获取锁失败，直接返回
                return null;
            }
            //抢到锁，继续执行
            // 2. 没有缓存 根据 id 查询到题目
            Question question = questionService.getOne(
                    Wrappers.lambdaQuery(Question.class).eq(Question::getAppId, appId)
            );
            QuestionVO questionVO = QuestionVO.objToVo(question);
            List<QuestionContentDTO> questionContent = questionVO.getQuestionContent();
            // 3. 调用 AI 获取结果
            // 封装 Prompt
            String userMessage = getAiTestScoringUserMessage(app, questionContent, choices);
            // AI 生成
            String result = aiManager.doSyncStableRequest(QUESTION_SOLVING_TEST_SYSTEM_PROMPT, userMessage);
            // 结果处理
            int start = result.indexOf("{");
            int end = result.lastIndexOf("}");
            String json = result.substring(start, end + 1);
            //4. 构建缓存
            answerCacheMap.put(cacheKey, json);
            // 5. 构造返回值，填充答案对象的属性
            UserAnswer userAnswer = JSONUtil.toBean(json, UserAnswer.class);
            userAnswer.setAppId(appId);
            userAnswer.setAppType(app.getAppType());
            userAnswer.setScoringStrategy(app.getScoringStrategy());
            userAnswer.setChoices(jsonStr);
            return userAnswer;
        } catch (InterruptedException e) {
            log.error("获取锁异常", e);
            throw new RuntimeException(e);
        } finally {
            // 释放锁
            if (lock != null && lock.isLocked()){
                if (lock.isHeldByCurrentThread()){
                    lock.unlock();
                }
            }
        }
    }

    /**
     * 拼接ai评分的用户信息
     * @param app
     * @param questionContentDTOList
     * @param choices
     * @return
     */
    private String getAiTestScoringUserMessage(App app, List<QuestionContentDTO> questionContentDTOList, List<String> choices) {
        StringBuilder userMessage = new StringBuilder();
        userMessage.append(app.getAppName()).append("\n");
        userMessage.append(app.getAppDesc()).append("\n");
        List<QuestionAnswerDTO> questionAnswerDTOList = new ArrayList<>();
        for (int i = 0; i < questionContentDTOList.size(); i++) {
            QuestionAnswerDTO questionAnswerDTO = new QuestionAnswerDTO();
            questionAnswerDTO.setTitle(questionContentDTOList.get(i).getTitle());
            questionAnswerDTO.setUserAnswer(choices.get(i));
            questionAnswerDTOList.add(questionAnswerDTO);
        }
        userMessage.append(JSONUtil.toJsonStr(questionAnswerDTOList));
        return userMessage.toString();
    }

    /**
     * 构建缓存的key
     * @param appId
     * @param choices
     * @return
     */
    private String buildCacheKey(Long appId, List<String> choices) {
        return DigestUtil.md5Hex(appId +":"+ choices);
    }
}
